package cn.outsourcing.supermarket.controller;

import cn.felord.payment.wechat.enumeration.TradeBillType;
import cn.felord.payment.wechat.v3.WechatApiProvider;
import cn.felord.payment.wechat.v3.WechatDirectPayApi;
import cn.felord.payment.wechat.v3.model.*;
import cn.outsourcing.supermarket.common.Result;
import cn.outsourcing.supermarket.common.constants.PayConstants;
import cn.outsourcing.supermarket.common.constants.WechatConstants;
import cn.outsourcing.supermarket.common.utils.DataUtils;
import cn.outsourcing.supermarket.common.utils.JwtUtils;
import cn.outsourcing.supermarket.common.utils.ResultUtils;
import cn.outsourcing.supermarket.controller.request.PayRequest;
import cn.outsourcing.supermarket.common.constants.Channel;
import cn.outsourcing.supermarket.common.enums.ChannelEnum;
import cn.outsourcing.supermarket.po.HiolabsOrder;
import cn.outsourcing.supermarket.po.HiolabsUser;
import cn.outsourcing.supermarket.service.*;
import cn.outsourcing.supermarket.vo.OrderGoodsVO;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.Month;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * 支付接口开发样例，以小程序支付为例.
 */
@Slf4j
@Profile({"wechat", "dev"})
@RestController
@RequestMapping("/marketing")
public class PayController {
    @Autowired
    private WechatApiProvider wechatApiProvider;
    @Autowired
    private HiolabsOrderService hiolabsOrderService;
    @Autowired
    private HiolabsUserService userService;
    @javax.annotation.Resource
    private HiolabsGoodsService goodsService;
    @javax.annotation.Resource
    private HiolabsOrderGoodsService orderGoodsService;
    @javax.annotation.Resource
    private HiolabsProductService productService;
    String TENANT_ID = "mobile";

    /**
     * 总流程建议为 生成商品订单 -> 生成对应的支付订单 -> 支付操作 -> 支付结果回调更新 -> 结束
     *
     * <p>此处建议在商品订单生成之后调用
     *
     * @param orderId 商品订单id
     * @return the object node
     */
    @PostMapping("/js")
    public Result js(@RequestBody PayRequest payRequest, HttpServletRequest request) {

        // TODO
        // 查询该orderId下是否生成了支付订单
        // 如果没有
        // 新增支付订单存入数据库 并标明支付状态为【待支付】
        // 根据新生成的支付订单信息向微信支付发起支付 并根据返回结果进行处理
        // 如果有状态为待支付
        // 根据待支付订单信息向微信支付发起支付 并根据返回结果进行处理
        // 如果有状态为待支付之外的状态
        // 根据产品的业务设计自行实现
        // 支付状态更新逻辑在【回调接口 /wxpay/callbacks/transaction】中处理  需要幂等处理

        // 开发时需要指定使用的商户租户配置 这里为 mobile 请参考 application-wechat.yml

        // 查询订单
        HiolabsOrder hiolabsOrder = hiolabsOrderService.getById(payRequest.getOrderId());
        if (Objects.isNull(hiolabsOrder)) {
            return Result.builder().errno(-500).errmsg("订单为空").build();
        }
        if (!Sets.newHashSet(PayConstants.NOT_PAYMENT, PayConstants.PAY_INT)
                .contains(hiolabsOrder.getOrderStatus())) {
            return Result.builder().errno(-501).errmsg("订单状态错误").build();
        }

        // 如果该商品免费，意味着用户使用了积分，则直接走支付成功扣除积分的流程。
        if (hiolabsOrder.getActualPrice().compareTo(BigDecimal.ZERO) == 0) {
            BigDecimal usedIntegral =
                    DataUtils.getUsedIntegral(hiolabsOrder.getOrderPrice(), hiolabsOrder.getActualPrice());
            HiolabsUser userMsg = userService.getUserMsg(JwtUtils.getTokenByRequest(request));
            userMsg.setCredits(userMsg.getCredits().subtract(usedIntegral));
            userService.updateById(userMsg);
            return ResultUtils.simpleResult(
                    hiolabsOrderService.updateFreeOrder(Integer.parseInt(payRequest.getOrderId())));
        }
        switch (hiolabsOrder.getChannel()) {
            case Channel.WECHAT_JSAPI:
                return weiXinPay(hiolabsOrder, payRequest, request);
            case Channel.OFFLINE_REMIT:
                return offlineRemit(hiolabsOrder, payRequest, request);
            default:
                return Result.builder().errno(-501).errmsg("订单支付渠道错误").build();
        }
    }

    private Result offlineRemit(HiolabsOrder hiolabsOrder, PayRequest payRequest, HttpServletRequest request) {
        HiolabsUser userMsg = userService.getUserMsg(JwtUtils.getTokenByRequest(request));
        BigDecimal balance = userMsg.getBalance();
        BigDecimal actualPrice = Optional.ofNullable(hiolabsOrder.getActualPrice())
                .orElse(BigDecimal.ZERO);
        if (balance.compareTo(actualPrice) < 0) {
            return Result.builder().errno(-501).errmsg("钱包余额不足").build();
        }
        userMsg.setBalance(userMsg.getBalance().subtract(actualPrice));
        // FIXME：使用积分后需要对用户进行积分扣除。
        BigDecimal integral =
                Optional.ofNullable(actualPrice)
                        .map(e -> e.divide(BigDecimal.valueOf(10), 2, RoundingMode.HALF_UP))
                        .orElse(BigDecimal.ZERO);
        // TODO：应当扣除的积分
        BigDecimal shouldSubtractIntegral =
                DataUtils.getUsedIntegral(hiolabsOrder.getOrderPrice(), hiolabsOrder.getActualPrice());
        userMsg.setCredits(
                userMsg.getCredits().add(integral).subtract(shouldSubtractIntegral));
        //                        byId.setCredits(byId.getCredits().add(integral));
        userService.updateById(userMsg);
        hiolabsOrder.setOrderStatus(PayConstants.PAYMENT_HAS_BEEN);
        hiolabsOrder.setPayStatus(true);
        hiolabsOrder.setPayTime(DataUtils.getSeconds());
        hiolabsOrder.setPayId(hiolabsOrder.getPayId());
        hiolabsOrder.setChannel(ChannelEnum.OFFLINE_REMIT.getIndex());
        hiolabsOrderService.updateById(hiolabsOrder);
        // 操作销量与库存
        List<OrderGoodsVO> orderGoods = orderGoodsService.getOrderGoods(hiolabsOrder.getId());
        for (OrderGoodsVO orderGood : orderGoods) {
            goodsService.reduceStock(orderGood.getGoodsId(), orderGood.getNumber());
            productService.reduceStock(hiolabsOrder.getId(), orderGood.getNumber());
        }
        return ResultUtils.simpleResult("支付成功");
    }

    private Result weiXinPay(HiolabsOrder hiolabsOrder, PayRequest payRequest, HttpServletRequest request) {
        if (!Objects.equals(hiolabsOrder.getChannel(), ChannelEnum.WECHAT_JSAPI.getIndex())) {
            return Result.builder().errno(-501).errmsg("订单支付渠道错误").build();
        }
        PayParams payParams = new PayParams();
        payParams.setDescription("订单号:" + hiolabsOrder.getOrderSn());
        // 商户侧唯一订单号 建议为商户侧支付订单号 订单表主键 或者唯一标识字段 [0-9a-zA-Z_\\-\\|\\*]+$
        payParams.setOutTradeNo(hiolabsOrder.getOrderSn());
        // 需要定义回调通知
        payParams.setNotifyUrl("/wxpay/callbacks/transaction");
        Amount amount = new Amount();
        // 支付金额
        amount.setTotal(
                Optional.ofNullable(hiolabsOrder.getActualPrice())
                        .map(e -> e.multiply(new BigDecimal("100")))
                        .map(BigDecimal::intValue)
                        .orElse(0));
        //    amount.setTotal(1);
        payParams.setAmount(amount);
        // 此类支付  Payer 必传  且openid需要同appid有绑定关系 具体去看文档
        Payer payer = new Payer();
        payParams.setPayer(payer);
        Object openId =
                JwtUtils.settings()
                        .build()
                        .checkJwt(JwtUtils.getTokenByRequest(request))
                        .get(WechatConstants.OPEN_ID);
        payer.setOpenid(String.valueOf(openId));
        if (Objects.isNull(openId)) {
            return Result.builder().build().defaultError();
        }
        log.info(
                "发起支付,TENANT_ID:{},openId:{},orderId{},outTradeNo:{}",
                TENANT_ID,
                openId,
                payRequest.getOrderId(),
                hiolabsOrder.getOrderSn());
        return ResultUtils.simpleResult(
                wechatApiProvider.directPayApi(TENANT_ID).jsPay(payParams).getBody());
    }

    /**
     * 下载对账单 如果要解析内容的话自行实现{"orderId":"1363"}
     *
     * @return the response entity
     */
    @GetMapping("/tradebill")
    public ResponseEntity<Resource> download() {
        WechatDirectPayApi wechatDirectPayApi = wechatApiProvider.directPayApi(TENANT_ID);

        TradeBillParams tradeBillParams = new TradeBillParams();
        tradeBillParams.setBillDate(LocalDate.of(2021, Month.MAY, 20));
        tradeBillParams.setBillType(TradeBillType.ALL);
        return wechatDirectPayApi.downloadTradeBill(tradeBillParams);
    }

    /**
     * 下载申请资金账单 如果要解析内容的话自行实现
     *
     * @return the response entity
     */
    @GetMapping("/fundflowbill")
    public ResponseEntity<Resource> fundFlowBill() {
        WechatDirectPayApi wechatDirectPayApi = wechatApiProvider.directPayApi(TENANT_ID);

        FundFlowBillParams fundFlowBillParams = new FundFlowBillParams();
        fundFlowBillParams.setBillDate(LocalDate.of(2021, Month.MAY, 20));

        return wechatDirectPayApi.downloadFundFlowBill(fundFlowBillParams);
    }
}
